<?php
abstract class NoBusinessBase_Model_BaseMapper
{
   public function __construct(array $options = null)
   {
      $this->_historyDate = isset($options["historyDate"]) ? $options["historyDate"] : null;
   }

   public function getIndividualFieldValues(NoBusinessBase_Model_Base & $pModel, $feilds, $modelName = null, $id = null)
   {
      if($id === null)
         $id = $pModel->getId();
      if(!$id)
         return;

      if($modelName === null)
         $modelName = $this->modelName();
      
      $pDb     = $this->getDbTable();
      $pSelect = $pDb->select()->setIntegrityCheck(false);
      $pSelect -> from($modelName, $feilds);
      $pSelect -> where('"Id" = ?', $id);
      
      $pRow = $pDb->fetchRow($pSelect);
      $this->setRow($pDb->fetchRow($pSelect), $pModel);
   }

   public function fetchMostRecentHistorical(NoBusinessBase_Model_Base $pModel)
   {
      if(in_array("PrimeId", $pModel::modelProps()))
         $id = $pModel->getPrimeId();

      $pSelect = $this->getSelect();
      $modelName = $this->modelName();
      $pSelect->where('"' . $modelName . '"."PrimeId" = ?', $pModel->getPrimeId());
      $pSelect->where('"' . $modelName . '"."ObjectVersion" = ?', $pModel->getObjectVersion());
      $pSelect->where('"' . $modelName . '"."Operation" IS NOT NULL');
      return $this->makeModel($pSelect);
   }

   protected function localFetchById($id, NoBusinessBase_Model_BaseMapper & $pObj = null)
   {
      // Return true so that fetchById keep running
      return true;
   }
   protected function preDelete(NoBusinessBase_Model_Base $pModel)
   {
      // Stub for children to implement
      return true;
   }
   protected function postDelete(NoBusinessBase_Model_Base $pDeletedModel, $success)
   {
      // Stub for children to implement
   }
   protected function preSave(NoBusinessBase_Model_Base &$pModel)
   {
   }

   protected function postInsert(NoBusinessBase_Model_Base &$pModel)
   {
   }

   protected function postSave(NoBusinessBase_Model_Base &$pModel, $isNew = false)
   {
      if($isNew)
         return;

      $cacheId = $this->getKeyNameById($pModel->getId());
      if($cacheId)
         $this->saveToCache($pModel, $cacheId);
   }

   protected function postSetRow($row, NoBusinessBase_Model_Base & $pModel)
   {
   }

   // This function is called before the DB save call.
   // You can implement this function in your base class
   // to massage the data being saved to the data base.
   //    E.g. Json encode an object as a string before save
   protected function postGetDataAsArray(& $data, NoBusinessBase_Model_Base & $pModel)
   {
      // Stub for children to implement
   }
   
   public function save(NoBusinessBase_Model_Base & $pModel, $forceNew = false)
   {
      if(!$this->userHasPermission($pModel, "edit"))
         new NoBusinessBase_Utility_Status(NoBusinessBase_Utility_Status::STATUS_BAD, "User does not have permission to save this model.", true);

      //We decided to disable permissions for notifiers.
      $disabled = Application_Model_Base_BaseMapper::permissionsDisabled();
      Application_Model_Base_BaseMapper::disablePermissions();

      $this->preSave($pModel);
      $data = $this->getDataAsArray($pModel, true);
      $id = $pModel->getId();
      
      $isNew = false;
      if (null == $id || $forceNew === true)
      {
         $isNew = true;
         $data['Id'] = $id;
         $id = $this->getDbTable()->insert($data);
         
         $pModel->setId($id);
         $this->postInsert($pModel);
         foreach($pModel->notifiers() as $pNotifier)
            $pNotifier->notifyCreate($pModel);

      }
      else
      {
         $this->getDbTable()->update($data);
         foreach($pModel->notifiers() as $pNotifier)
            $pNotifier->notifyModify($pModel);
      }
      
      $this->postSave($pModel, $isNew);

      if(!$disabled)
         Application_Model_Base_BaseMapper::enablePermissions();

      return $id;
   }

   protected function createModelFromRow(Zend_Db_Table_Row $row = null, $extraFields = false)
   {
      if($row === null)
         return null;
      
      $className = "Application_Model_" . ucfirst($this->modelName());
      $pModel = new $className;
      $this->setRow($row, $pModel, $extraFields);
      if(!$this->userHasPermission($pModel, "view"))
         return null;

      return $pModel;

   }

   public function userHasPermission(NoBusinessBase_Model_Base & $pModel, $permissionType)
   {
      return true;
   }

   public function getCount(Zend_Db_Table_Select $pSelect, $castFloat = false)
   {
      $rows = $this->getDbTable()->fetchAll($pSelect);
      if(isset($rows[0]) && isset($rows[0]->_count))
         return $castFloat ? (float)$rows[0]->_count : (int)$rows[0]->_count;
      return false;
   }

   public function makeModel(Zend_Db_Table_Select $pSelect, $extraFields = false)
   {
try{
      $row = $this->getDbTable()->fetchRow($pSelect);
      return $this->createModelFromRow($row, $extraFields);
} catch (Exception $e)
{
   var_dump($e->getTrace());
   echo PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL;
	echo __FILE__.'('.__LINE__.'):'.PHP_EOL . $pSelect; exit;
}
   }

   public function makeModels(Zend_Db_Table_Select $pSelect, $extraFields = false)
   {
try{
      $rows = $this->getDbTable()->fetchAll($pSelect);
      $models = array();
      foreach($rows as $row)
      {
         $pModel = $this->createModelFromRow($row, $extraFields);
         if($pModel)
            $models[] = $pModel;
      }
      return $models;
} catch (Exception $e)
{
   var_dump($e->getTrace());
   echo PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL;
	echo __FILE__.'('.__LINE__.'):'.PHP_EOL . $pSelect; exit;
}
   }

   protected function setRow($row, NoBusinessBase_Model_Base & $pModel, $extraFields = false)
   {
      if($extraFields)
      {
         $rowArray = $row->toArray();
         foreach($rowArray as $column => $value)
         {
            if(strlen($column) <= 1 || $column[0] !== '_' || $column[1] !== '_')
               continue;

            $pModel->addExtraField($column, $value);
         }
      }

      $this->postSetRow($row, $pModel);
   }

   public function deleteId($passedId)
   {
      if(is_object($passedId))
      {
         $pModel = $passedId;
         $passedId = $pModel->getId();
      }
      else
      {
         $pModel = $this->fetchById($passedId);
      }

      return $this->delete($pModel);
   }
   
   public function delete(NoBusinessBase_Model_Base &$pModel)
   {
      if(!$pModel || !$this->userHasPermission($pModel, "delete"))
         new NoBusinessBase_Utility_Status(NoBusinessBase_Utility_Status::STATUS_BAD, "User does not have permission to delete this model.", true);

      if(!$this->preDelete($pModel))
         return false;

      $data = $this->getDataAsArray($pModel, true);
      $fullModel = $pModel->toArray();
      $success = $this->getDbTable()->delete($data, $fullModel);
      foreach($pModel->notifiers() as $pNotifier)
         $pNotifier->notifyDelete($pModel);
      $this->removeFromCache($this->getKeyNameById($pModel->getId()));
      $this->postDelete($pModel, $success);

      return $success;
   }
   
   public function find($id, NoBusinessBase_Model_Base $pModel)
   {
      $result = $this->getDbTable()->find($id);
      if (0 == count($result))
         return false;

      $row = $result->current();
      $this->setRow($row, $pModel);
      return true;
   }
   
   public function fetchAll(NoBusinessBase_Utility_SortAndRangeRequest $pSortAndRangeRequest = null, $includeHistory = false, $includeExtraFields = false)
   {
      if($pSortAndRangeRequest)
         $pSelect = $this->getSelect($pSortAndRangeRequest);
      else
         $pSelect = $this->getSelect();

      if(!$includeHistory)
      {
         $modelName = $this->modelName();
         $className = "Application_Model_" . ucfirst($modelName);
         if(in_array("Operation", $className::modelProps()))
            $pSelect->where('"' . $modelName . '"."Operation" IS NULL');
      }

      return $this->makeModels($pSelect, $includeExtraFields);
   }

   public function fetchById($id)
   {
      $modelName = $this->modelName();
      $className = "Application_Model_" . ucfirst($modelName);
      $idcol = "Id";
      if(in_array("PrimeId", $className::modelProps()))
         $idcol = "PrimeId";

      $pSelect = $this->getSelect();
      if($id !== null)
         $pSelect->where('"' . $this->modelName() . '"."' . $idcol . '" = ?', $id);
      else
         $pSelect->where('"' . $this->modelName() . '"."' . $idcol . '" IS NULL', $id);

      return $this->makeModel($pSelect);
   }

   public function getKeyNameById($id)
   {
      return 'db_' . ucfirst($this->modelName()) . '_' . $id;
   }

   public function getKeyName($key)
   {
      $key = str_replace(array(':','-'), '_', $key);
      return 'db_' . ucfirst($this->modelName()) . '_' . $key;
   }

   protected function setRows($resultSet)
   {
      $entries    = array();
      foreach ($resultSet as $row)
      {
         $className = "Application_Model_" . ucfirst($this->modelName());
         $entry = new $className;
         $this->setRow($row, $entry);
         $entries[] = $entry;
      }
      return $entries;
   }



# =============================================================================
# Select Stuff
# =============================================================================
   public $_historyDate;

   public function setupSelect(Zend_Db_Select & $pSelect, NoBusinessBase_Utility_SortAndRangeRequest $pSortAndRange = null, $count = false, $autoFrom = true, $autoJoin = true)
   {
      if($autoFrom)
      {
         if(!$count)
            $pSelect->from($this->modelName());
         else
            $pSelect->from($this->modelName(), array('COUNT(*) AS _count'));
            #$pSelect->from($this->modelName(), array('COUNT("'.$this->modelName().'"."Id") AS _count'));
      }
      if($autoJoin)
         $this->addJoins($pSelect, $count);
      if($pSortAndRange)
      {
         $pSelect->limit($pSortAndRange->getLimit(), $pSortAndRange->getStart());
         $pSelect->order($pSortAndRange->getSort());
      }
      return $pSelect;
   }

   public function getSelect(NoBusinessBase_Utility_SortAndRangeRequest $pSortAndRange = null, $count = false, $autoFrom = true, $autoJoin = true)
   {
      $pSelect = $this->getDbTable()->autoSelect(["historyDate" => $this->_historyDate])->setIntegrityCheck(false);
      return $this->setupSelect($pSelect, $pSortAndRange, $count, $autoFrom, $autoJoin);
   }

   public function getAutoSelect(NoBusinessBase_Utility_SortAndRangeRequest $pSortAndRange = null, $count = false, $autoFrom = true, $autoJoin = true)
   {
      $pSelect = $this->getDbTable()->autoSelect(["historyDate" => $this->_historyDate])->setIntegrityCheck(false);
      return $this->setupSelect($pSelect, $pSortAndRange, $count, $autoFrom, $autoJoin);
   }

   public function getRawSelect()
   {
      return $this->getDbTable()->rawSelect()->setIntegrityCheck(false);
   }

   protected function addJoins(Zend_Db_Select & $pSelect, $count = false)
   {
   }



# =============================================================================
# Cache stuff
# =============================================================================
   public function removeFromCache($id)
   {
      if(!Zend_Registry::isRegistered('Memcache'))
         return;
      
      $pMemcache = Zend_Registry::get('Memcache');
      if(!$pMemcache)
         return;

      $pMemcache->remove($id);
   }
   
   public function loadFromCache($id)
   {
      if(!Zend_Registry::isRegistered('Memcache'))
         return null;

      $pMemcache = Zend_Registry::get('Memcache');
      if(!$pMemcache)
         return null;
      
      $pResult = $pMemcache->load($id);
      if($pResult)
         return $pResult;

      // not found in cache, look it up in the database
      return null;
   }
   
   public function saveToCache($pObj, $id)
   {
      if(!Zend_Registry::isRegistered('Memcache'))
         return false;
      $pMemcache = Zend_Registry::get('Memcache');
      if(!$pMemcache)
         return false;

      // Save the result in the cahe so future lookup events will find it
      return $pMemcache->save($pObj, $id);
   }

# =============================================================================
# NOTE(Keith): This should be deleted
# =============================================================================
   protected function setupHistory(Zend_Db_Select & $pSelect, array $refs)
   {
      if(!count($refs))
         return;

      $historyRef = false;
      $modelName = $this->modelName();
      $joinSel = $this->getDbTable()->select()->setIntegrityCheck(false);
      foreach($refs as $modelRefKey => $refInfo)
      {
         if(!is_array($refInfo))
         {
            $pRefModel = $refInfo;
            $join = true;
            $on = $modelName;
         }
         else
         {
            $pRefModel = $refInfo["Model"];
            $join = !isset($refInfo["Join"]) || $refInfo["Join"] !== false;
            $on = isset($refInfo["On"]) ? $refInfo["On"] : $modelName;
         }
         $refModelName = $pRefModel->modelName();
         #$refModelId = $pRefModel->getId();
         #if(!$pRefModel->getOperation())
         #{
         #   $pSelect->where('"' . $on . '"."' . $modelRefKey . '" = ?', $refModelId);
         #   continue;
         #}
         $historyRef = true;

         #$joinSel->where('"N"."' . $modelRefKey . '" <= ?', $refModelId);
         if($join)
            $pSelect->join($refModelName, '"' . $refModelName . '"."PrimeId" = "' . $on . '"."' . $modelRefKey . '"', array());
         $pSelect->where('"' . $refModelName . '"."PrimeId" = ?', $pRefModel->getPrimeId());
         #$pSelect->where('"' . $on . '"."' . $modelRefKey . '" <= ?', $refModelId);
      }
      if(!$historyRef)
         return;

      #$joinSel->where('"N"."PrimeId" = "' . $modelName . '"."PrimeId"');
      #$joinSel->where('"N"."Id" > "' . $modelName . '"."Id"');
      #$joinSel->where('"N"."Operation" IS NOT NULL');
      #$conditions = $joinSel->getPart(Zend_Db_Select::WHERE);
      #$pSelect->joinLeft(array('N' => $modelName), implode(' ', $conditions), array());
      #$pSelect->where('"N"."Id" IS NULL');
      #$pSelect->where('"' . $modelName . '"."Operation" IN (?)', array("Create", "Modify"));
   }
}

